package main

import (
	"fmt"
	"sync"
	"time"
)

/*
  通道是什么，通道就是goroutine之间的通道。它可以让goroutine之间相互通信。


  通道的注意点:
  	1.用于goroutine，传递消息的。

	2.通道，每个都有相关联的数据类型,
		nil chan，不能使用，类似于nil map，不能直接存储键值对

	3.使用通道传递数据：<-
		chan <- data,发送数据到通道。向通道中写数据
		data <- chan,从通道中获取数据。从通道中读数据

	4.阻塞：
		发送数据：chan <- data,阻塞的，直到另一条goroutine，读取数据来解除阻塞
		读取数据：data <- chan,也是阻塞的。直到另一条goroutine，写出数据解除阻塞。

	5.本身channel就是同步的，意味着同一时间，只能有一条goroutine来操作。

*/
func main() {

	// 声明一个通道
	var a chan int

	if a == nil {
		fmt.Println("channel 是 nil 的, 不能使用，需要先创建通道。。")
		a = make(chan int)
		fmt.Printf("数据类型是： %T", a)
	}
	fmt.Println("--------------------------------------")

	// 也可这么声明
	// b := make(chan int)

	// channel是引用类型的数据，在作为参数传递的时候，传递的是内存地址。
	ch01 := make(chan int)
	fmt.Printf("%T,%p\n", ch01, ch01)

	test01(ch01)

	// 发送和接收
	// read from channel a
	//data := <- a
	//从一个channel中读取
	// v, ok := <- a

	// write to channel a
	//a <- data

	/*
	   发送和接收默认是阻塞的
	   一个通道发送和接收数据，默认是阻塞的。当一个数据被发送到通道时，在发送语句中被阻塞，直到另一个Goroutine从该通道读取数据。
	   相对地，当从通道读取数据时，读取被阻塞，直到一个Goroutine将数据写入该通道。
	*/

	// 声明，没有创建
	var ch02 chan bool
	fmt.Println(ch02)        //
	fmt.Printf("%T\n", ch02) //chan bool
	ch02 = make(chan bool)   //0xc000050120,是引用类型的数据
	fmt.Println(ch02)

	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println("子goroutine中，i：", i)
		}
		ch02 <- true
		fmt.Println("结束......")
	}()

	// 从ch02通道中读取数据
	/*
	  这一行代码是阻塞的，这意味着在子Goroutine将数据写入到该通道之前，主goroutine将不会执行到下一行代码。
	*/
	data := <-ch02
	fmt.Println("data-->", data)
	fmt.Println("main.....over.....")

	/*
	  死锁
	  使用通道时要考虑的一个重要因素是死锁。如果Goroutine在一个通道上发送数据，那么预计其他的Goroutine应该接收数据。
	  如果这种情况不发生，那么程序将在运行时出现死锁。
	*/

	// ch03 := make(chan int)
	// ch03 <- 5

	/*
	   关闭通道
	   发送者可以通过关闭信道，来通知接收方不会有更多的数据被发送到channel上。
	   close(ch)


	   v, ok := <- ch
	   如果ok的值是true，表示成功的从通道中读取了一个数据value。如果ok是false，这意味着正在从一个封闭的通道读取数据

	*/

	ch04 := make(chan int)
	go sendData(ch04)
	/*
	   子goroutine，写出数据10个
	           每写一个，阻塞一次，主程序读取一次，解除阻塞

	   主goroutine：循环读
	           每次读取一个，堵塞一次，子程序，写出一个，解除阻塞
	*/
	//主程序中获取通道的数据
	for {
		time.Sleep(1 * time.Second)
		//其他goroutine，显示的调用close方法关闭通道
		v, ok := <-ch04
		// 判断通道是否关闭
		if !ok {
			fmt.Println("已经读取了所有的数据，", ok)
			break
		}
		fmt.Println("common for 取出数据：", v, ok)
	}

	fmt.Println("common...main...over....")

	fmt.Println("---------------------------------------------------")

	/*
	   通道上的范围循环
	   可以循环从通道上获取数据，直到通道关闭。for循环的for range形式可用于从通道接收值，直到它关闭为止。
	*/
	ch07 := make(chan int)
	go sendData(ch07)
	// for循环的for range形式可用于从通道接收值，直到它关闭为止。
	for v := range ch07 {
		fmt.Println("rang for 读取数据：", v)
	}
	fmt.Println("range...main..over.....")

	fmt.Println("start: go channel 类型基本使用")

	// 只发送channel类型
	ch08 := make(chan<- int, 1)
	fmt.Printf("ch08 类型 :%T, value: %v\n", ch08, ch08)
	// // 只接收channel类型
	ch09 := make(<-chan int, 1)
	fmt.Printf("ch09 类型 :%T, value: %v\n", ch09, ch09)
	fmt.Println("end: go channel 类型基本使用")

	fmt.Println("start: go channel 生产/消费者基本使用")

	ch10 := make(chan int, 5)
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		producer(ch10)
		wg.Done()
	}()

	go func() {
		// 可以准确地判定 channel 是否被关闭
		// m, ok := <-ch10
		consumer(ch10)
		wg.Done()
	}()

	wg.Wait()

	fmt.Println("end: go channel 生产/消费者基本使用")

	fmt.Println("start: go 用作信号传递")

	println("start a worker...")
	ch11 := spawn(work)
	<-ch11
	println("end the worker...")
	fmt.Println("end: go 用作信号传递")

	fmt.Println("start: go基于同步锁实现并发安全")

	var wg02 sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg02.Add(1)
		go func(i int) {
			v := increase()
			fmt.Printf("goroutine-%d: current counter value is %d\n", i, v)
			wg02.Done()
		}(i)
	}
	wg02.Wait()
	fmt.Println("end: go基于同步锁实现并发安全")

	fmt.Println("start: go基于channel实现并发安全")

	var wg03 sync.WaitGroup
	var count = NewCounter()
	for i := 0; i < 10; i++ {
		wg03.Add(1)
		go func(i int) {
			v := count.chanIncrease()
			fmt.Printf("goroutine-%d: current counter value is %d\n", i, v)
			wg03.Done()
		}(i)
	}
	wg03.Wait()
	fmt.Println("end: go基于channel实现并发安全")

}

func test01(ch chan int) {
	fmt.Printf("%T,%p\n", ch, ch)
}

func sendData(ch chan int) {
	// 发送方：10条数据
	for i := 0; i < 10; i++ {
		ch <- i //将i写入通道中
	}
	close(ch)
}

// 生产者只能向 channel 中发送数据，使用chan<- int作为 produce 函数的参数类型
func producer(ch chan<- int) {
	for i := 0; i < 10; i++ {
		ch <- i + 1
		time.Sleep(time.Second)
	}
	close(ch)
}

// 消费者只能从 channel 中接收数据，使用<-chan int作为 consume 函数的参数类型
func consumer(ch <-chan int) {
	//  for range 循环语句来从 channel 中接收数据，for range 会阻塞在对 channel 的接收操作上，
	//  直到 channel 中有数据可接收或 channel 被关闭循环，才会继续向下执行。channel 被关闭后，for range 循环也就结束了
	for n := range ch {
		println(n)
	}
}

type signal struct {
}

func work() {
	println("worker is working...")
	time.Sleep(1 * time.Second)
}

// spawn 函数返回的 channel，被用于承载新 Goroutine 退出的“通知信号”，
// 这个信号专门用作通知 main goroutine。main goroutine 在调用 spawn 函数后一直阻塞在对这个“通知信号”的接收动作上
func spawn(f func()) <-chan signal {
	ch := make(chan signal)
	go func() {
		println("worker start to work...")
		f()
		ch <- signal{}
	}()
	return ch
}

type counter struct {
	sync.Mutex
	i int
}

var cter counter

func increase() int {
	cter.Lock()
	defer cter.Unlock()
	cter.i++
	return cter.i
}

type chanCounter struct {
	ch chan int
	i  int
}

// 将计数器操作全部交给一个独立的 Goroutine 去处理，并通过无缓冲 channel 的同步阻塞特性，实现了计数器的控制。
// 这样其他 Goroutine 通过 Increase 函数试图增加计数器值的动作，实质上就转化为了一次无缓冲 channel 的接收动作
// 不要通过共享内存来通信，而是通过通信来共享内存”原则。
func NewCounter() *chanCounter {
	count := &chanCounter{
		ch: make(chan int),
	}
	go func() {
		for {
			count.i++
			count.ch <- count.i
		}
	}()
	return count
}

func (count *chanCounter) chanIncrease() int {
	return <-count.ch
}
